-- Copyright (c) 2025 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
--
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local os = require 'os'
local cjson = require 'cjson'

local m = {}

local TRUSTED_COMPONENT_PATH_FMT = "/bmc/kepler/Chassis/(%d+)/TrustedComponents/([_%w]+)"
local TRUSTED_COMPONENT_INTF = "bmc.kepler.RoT.TrustedComponent"
local TRUSTED_COMPONENT_CERT_PATH_FMT = "/bmc/kepler/Chassis/(%d+)/TrustedComponents/([_%w]+)/Certificates/(%d+)"
local COMPONENT_INTEGRITY_PATH_FMT = "/bmc/kepler/Chassis/(%d+)/TrustedComponents/([_%w]+)/ComponentIntegrity/(%d+)"
local MDB_COMPONENT_INTEGRITY_PATH_FMT = "/bmc/kepler/Chassis/%d/TrustedComponents/%s/ComponentIntegrity/%d"
local SPDM_MEASUREMENT_INTF = "bmc.kepler.RoT.ComponentIntegrity.SPDMMeasurement"
local CONTROLLER_PATH_FMT = "/bmc/kepler/Systems/(%d+)/Storage/Controllers/([_%w]+)"
local CONTROLLER_INTF = "bmc.kepler.Systems.Storage.Controller"

function m.get_trusted_components(chassis_id, components)
    local uri_prefix = '/redfish/v1/Chassis/' .. chassis_id .. '/TrustedComponents/'
    local members = {}
    local count = 0

    local component_name

    if components then
        for _, v in pairs(components) do
            _, component_name = string.match(v, TRUSTED_COMPONENT_PATH_FMT)
            count = count + 1
            members[count] = {["@odata.id"] = uri_prefix .. component_name}
        end
    end

    return {count = count, members = members}
end

function m.get_certificates(chassis_id, trusted_component_id, certs)
    local uri_prefix = '/redfish/v1/Chassis/' .. chassis_id .. '/TrustedComponents/' .. trusted_component_id
        .. '/Certificates/'
    local members = {}
    local count = 0

    local slot_id
    if certs then
        for _, v in pairs(certs) do
            _, _, slot_id = string.match(v, TRUSTED_COMPONENT_CERT_PATH_FMT)
            count = count + 1
            members[count] = {["@odata.id"] = uri_prefix .. tostring(slot_id)}
        end
    end

    return {count = count, members = members}
end

function m.get_component_integrity(paths)
    local uri_prefix = "/redfish/v1/ComponentIntegrity/"
    local members = {}
    local count = 0

    local component_id, integrity_id
    if paths then
        for _, path in pairs(paths) do
            _, component_id, integrity_id = string.match(path, COMPONENT_INTEGRITY_PATH_FMT)
            count = count + 1
            members[count] = {["@odata.id"] = uri_prefix .. tostring(component_id) .. "_" .. tostring(integrity_id)}
        end
    end

    return {count = count, members = members}
end

function m.get_component_integrity_path(integrity_id, paths)
    local flag = false
    local target_path = ""
    if not paths or not next(paths) then
        return {flag = flag, path = target_path}
    end

    local component_id, id, tmp_integrity_id

    for _, path in pairs(paths) do
        _, component_id, tmp_integrity_id = string.match(path, COMPONENT_INTEGRITY_PATH_FMT)
        id = tostring(component_id) .. "_" .. tostring(tmp_integrity_id)
        if integrity_id == id then
            flag = true
            target_path = string.format(MDB_COMPONENT_INTEGRITY_PATH_FMT, 1, component_id, tonumber(tmp_integrity_id))
            break
        end
    end

    return {flag = flag, path = target_path}
end

function m.get_integrity_description(path)
    local _, component_id, _ = string.match(path, COMPONENT_INTEGRITY_PATH_FMT)
    local pos = string.find(path, "/[^/]+/[^/]+$")
    local component_path = string.sub(path, 1, pos - 1)
    local component_obj = mdb.get_object(bus, component_path, TRUSTED_COMPONENT_INTF)

    return "SPDM Integrity Information for " .. component_obj.Name .. " reported by " .. component_id
end

function m.get_certificate_collection_path(integrity_path)
    local chassis_id, component_id, _ = string.match(integrity_path, COMPONENT_INTEGRITY_PATH_FMT)
    return '/redfish/v1/Chassis/' .. chassis_id .. '/TrustedComponents/' .. component_id .. '/Certificates'
end

function m.get_certificate_path(slot_id, integrity_path)
    if slot_id == 15 then
        return lua_nil
    end
    local chassis_id, component_id, _ = string.match(integrity_path, COMPONENT_INTEGRITY_PATH_FMT)
    local path = '/redfish/v1/Chassis/' .. chassis_id .. '/TrustedComponents/' .. component_id .. '/Certificates/' .. (slot_id + 1)
    return {["@odata.id"] = path}
end

function m.time_int_to_string(time_int)
    return os.date("!%Y-%m-%dT%H:%M:%SZ", time_int)
end

function m.pack_measurements(specification, paths)
    local result = cjson.json_object_new_array()

    local obj
    local tmp_mea_obj
    for _, path in pairs(paths) do
        obj = mdb.get_object(bus, path, SPDM_MEASUREMENT_INTF)

        tmp_mea_obj = cjson.json_object_new_object()
        tmp_mea_obj.MeasurementIndex = obj.Index
        tmp_mea_obj.MeasurementHashAlgorithm = obj.HashAlgorithm
        tmp_mea_obj.Measurement = obj.Measurement
        tmp_mea_obj.LastUpdated = m.time_int_to_string(obj.LastUpdated)
        tmp_mea_obj.PartofSummaryHash = obj.PartofSummaryHash
        -- 仅当 specification 为 DMTF 时才呈现 MeasurementType
        if specification == "DMTF" then
            tmp_mea_obj.MeasurementType = obj.MeasurementType

            -- 仅当 MeasurementType 为 MutableFirmwareSecurityVersionNumber 时才呈现 SecurityVersionNumber
            if obj.MeasurementType == "MutableFirmwareSecurityVersionNumber" then
                tmp_mea_obj.SecurityVersionNumber = obj.SecurityVersionNumber
            end
        end

        result[#result + 1] = tmp_mea_obj
    end

    return result
end

function m.get_component_uri(path)
    local system_id, _ = string.match(path, CONTROLLER_PATH_FMT)
    local obj = mdb.get_object(bus, path, CONTROLLER_INTF)

    return "/redfish/v1/Systems/" .. tostring(system_id) .. "/Storages/RAIDStorage" .. tostring(obj.Id)
end

local function generate_random_hex_string(len)
    local hex_chars = "0123456789abcdefABCDEF"
    local result = ""
    local index
    for _ = 1, len do
        index = math.random(1, #hex_chars)
        result = result .. string.sub(hex_chars, index, index)
    end

    return result
end

function m.get_nonce(input_nonce)
    if not input_nonce then
        return generate_random_hex_string(64)
    end

    return input_nonce
end

return m